// type aliases for naming specific function signatures
type Filter = fn (string) string

// a function can accept an argument of a function type
fn filter(s string, f Filter) string {
	return f(s)
}

// duck-typing
//   don't need to declare compatibility with a function type
//   they just have to be compatible
// `uppercase` can be used everywhere where Filter is expected
fn uppercase(s string) string {
	return s.to_upper()
}

// explicitly cast to a function type
my_filter := Filter(uppercase)
println(filter('Hello world', my_filter))

// without an explicit cast, use duck-typing
my_filter2 := uppercase
println(filter('Hello world', my_filter2))

// pass it directly as well
println(filter('Hello world', uppercase))

// works with anonymous functions as well
println(filter('Hello world', fn (s string) string {
	return s.to_upper()
}))
